package ui;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.BoxLayout;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
 * A Panel that contains allows a user to input GPS 
 * coordinates. The panel contains methods to validate
 * the GPS coordinates
 * 
 * @author Sean Young
 * @version 1.0
 *
 */
public class GPSPanel extends JPanel{
	private JTextField gpsLat, gpsLong;
	private JPanel gpsPanel, verificationPanel;
	private JLabel verificationLabel;
	private GPSVerifier gpsVerifier = new GPSVerifier();
	private NumberFormat gpsFormat;
	private boolean isValidLat = true, isValidLong = true, latEmpty = true, longEmpty = true;
	
	public GPSPanel() {
		super();
		setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

		
		gpsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		verificationPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		
		gpsPanel.add(new JLabel("GPS:"));
		
		gpsFormat = NumberFormat.getNumberInstance();
		gpsFormat.setMaximumIntegerDigits(3);
		gpsFormat.setMaximumFractionDigits(6);
		
		gpsLat = new JTextField(10);
		gpsLat.setInputVerifier(gpsVerifier);
		gpsLat.addActionListener(gpsVerifier);
		
		gpsLong = new JTextField(10);
		gpsLong.setInputVerifier(gpsVerifier);
		gpsLong.addActionListener(gpsVerifier);
		
		gpsPanel.add(gpsLat);
		gpsPanel.add(gpsLong);
		
		verificationLabel = new JLabel("   ");
		verificationLabel.setForeground(Color.RED);
		verificationPanel.add(verificationLabel);
		
		add(gpsPanel);
		add(verificationPanel);
	}
	/**
	 * Resets the GPS panel
	 */
	public void reset() {
		gpsLat.setText("");
		gpsLat.setBackground(Color.WHITE);
		isValidLat = true;
		latEmpty = true;
		
		gpsLong.setText("");
		gpsLong.setBackground(Color.WHITE);
		isValidLong = true;
		longEmpty = true;
		
		
		verificationLabel.setText("   ");
	}
	
	/**
	 * Returns the latitude coordinates input by the user.
	 * @return The latitude coordinates input by the user.
	 */
	public float getLatitude() {
		return Float.parseFloat(gpsLat.getText());
	}
	
	/**
	 * Returns the longitude coordinates input by the user.
	 * @return The longitude coordinates input by the user.
	 */
	public float getLongitude() {
		return Float.parseFloat(gpsLong.getText());
	}
	
	/**
	 * Returns whether the Panel contains valid GPS Coordinates.
	 * @return Returns whether the coordinates are valid.
	 */
	public boolean hasValidCoordinates() {
		return (isValidLat && isValidLong && (!latEmpty) && (!longEmpty));
	}
	
	/**
	 * Returns whether the longitude and latitude fields are empty
	 * @return Whether the longitude and latitude fields are empty.
	 */
	public boolean isEmpty() {
		return (latEmpty && longEmpty);
	}
	
	/**
	 * Sets the boolean value indicating validity of a field
	 * @param comp The component the event originated from
	 * @param value The value to set the boolean to
	 */
	public void setGPSBool(JComponent comp, boolean value) {
		if(comp == gpsLong) {
			isValidLong = value;
		} else if (comp == gpsLat) {
			isValidLat = value;
		}
	}
	
	/**
	 * Sets the boolean value indicating whether or not a field
	 * is empty
	 * @param comp The component the event originated from
	 * @param value The value to set the boolean to
	 */
	public void setEmptyBool(JComponent comp, boolean value) {
		if(comp == gpsLong) {
			longEmpty = value;
		} else if (comp == gpsLat) {
			latEmpty = value;
		}
	}
	
	/**
	 * Verifies the validity of the GPS coordinates input by the user.
	 */
	public class GPSVerifier extends InputVerifier implements ActionListener {
		
		/**
		 * Verifies the validity of the field by checking
		 * a number of conditions based upon the component.
		 * @param comp The component to check
		 * @return The validity of the input
		 */
		@Override
		public boolean verify(JComponent comp) {
			if(((JTextField) comp).getText().equals("")) {
				setEmptyBool(comp, true);
				return true;
			}
			
			if(!(isValidGPSFormat(((JTextField) comp).getText()))) {
				verificationLabel.setText("Incorrect GPS Format. The number must be a valid GPS coordinate.");
				setEmptyBool(comp, false);
				return false;
			}
			
			if(!(isValidFloat(((JTextField) comp).getText()))) {
				verificationLabel.setText("Invalid Number. The number must be a valid GPS coordinate.");
				setEmptyBool(comp, false);
				return false;
			}
			
			if(!(isValidRange(comp))) {
				setEmptyBool(comp, false);
				return false;
			}
			
			setEmptyBool(comp, false);
			return true;
		}
		
		/**
		 * Returns whether the given string is a valid float
		 * @param number The string to be checked
		 * @return The validity of the string
		 */
		public boolean isValidFloat(String number) {
			try {
				Float f = Float.parseFloat(number);
				return true;
			} catch(Exception e) {
				return false;
			}
		}
		
		/**
		 * Returns whether the given string is in the correct
		 * format
		 * @param gps The string to be checked
		 * @return The validty of the string
		 */
		public boolean isValidGPSFormat(String gps) {
			if(gps == null) return false;
			try {
				Number n = gpsFormat.parse(gps);
				return true;
			} catch (ParseException e) {
				return false;
			}
		}
		
		/**
		 * Checks the value of the input and determines whether it is
		 * in the valid range of GPS coordinates
		 * @param comp The component to check
		 * @return Whether the input is within the range or not
		 */
		public boolean isValidRange(JComponent comp) {
			Float coordinate = Float.parseFloat(gpsLat.getText());
			if(comp == gpsLat) {
				if(coordinate >= -90 && coordinate <= 90) {
					return true;
				} else {
					verificationLabel.setText("Valid latitude coordinates must be a decimal ranging from -90 to 90");
					return false;
				}
			} else if (comp == gpsLong) {
				if(coordinate >= - 180 && coordinate <= 180) {
					return true;
				} else {
					verificationLabel.setText("Valid longitude coordinates must be a decimal ranging from -180 to 180");
					return false;
				}
			} else {
				return false;
			}
		}
		
		/**
		 * Returns whether the component should yield focus
		 * to other components. It will always evaluate to
		 * true to allow user to manipulate other fields
		 */
		public boolean shouldYieldFocus(JComponent comp) {
			boolean inputOK = verify(comp);
			if(inputOK) {
				verificationLabel.setText("    ");
				((JTextField) comp).setBackground(Color.WHITE);
				setGPSBool(comp, true);
				return true;
			} else {
				((JTextField) comp).setBackground(Color.PINK);
				setGPSBool(comp, false);
				return true;
			}
		}
		
		/**
		 * Performs the validation operations upon detecting
		 * an ActionEvent.
		 */
		public void actionPerformed(ActionEvent e) {
			JTextField source = (JTextField) e.getSource();
			shouldYieldFocus(source);
			source.selectAll();
		}
	}
}
